#pragma once
#include "gtest/gtest.h"

#include "Utility/FileSystem/SGFileSystem.h"
#include "DataStructure/Common/Id.h"
#include "DataStructure/Common/Real.h"

#include "TestData/model/OneBeam.hpp"
#include "TestData/model/Link_TwoELe.hpp"

#include "SGFem/Pre/Assembly/Assembly.h"
#include "SGFem/Pre/Assembly/CoordinateProcessor.h"
#include "Import/ImportAPI/ImportAPI.h"

#include "../LoadCalculator.h"

using SG::DataStructure::Common::Status;
using SG::DataStructure::Common::Id;
using SG::DataStructure::Common::Real;
using SG::DataStructure::Common::TOLERANCE;
using SG::FEMSolver::AssemblyEigen;
using SG::FEMSolver::CoordinateProcessor;
using SG::Import::SGImporter;

extern std::shared_ptr<DBServiceFactory> DB_SERVICEFACTORY;

void initialize (SG::DBManager::DBServiceFactorySP dbManager)
{
    auto& assembly = AssemblyEigen::GetInstance();
    assembly.Clear();
    auto para = dbManager->get<ISubcaseService> () ->FindSubCase (1);
    SG::DataStructure::FEM::Subcase subcase (para);
    assembly.Initialize (dbManager, subcase, {1});

    auto& coordHandler = CoordinateProcessor::GetInstance();
    coordHandler.Initialize (dbManager);
}


TEST (ComputeNodeLoadTest, MomentTest)
{
    // Input
    OneBeamCantilever model;
    auto dbManager = model.GetModel ();
    initialize (dbManager);

    auto& assembly = AssemblyEigen::GetInstance();
    assembly.Assembly (dbManager);

    // 模拟载荷计算
    std::set<Id> LoadCaseIds = {1};
    LoadCalculator::ComputeStaticLoad  (dbManager, LoadCaseIds);
    assembly.Constraint (DB_SERVICEFACTORY, 1);

    // 计算载荷向量
    SG::FEMSolver::EigenVector fTest;
    assembly.ComputeBVector(1, _OUT fTest);

    //Check
    ASSERT_NEAR (320.0, fTest(2), TOLERANCE);
    ASSERT_NEAR (290.0, fTest(4), TOLERANCE);
}


void GetModel (const std::string& fileName,const std::string& /*jobName*/, SG::DBManager::DBServiceFactorySP& dbServiceFactory)
{
    dbServiceFactory->ClearDatabase ();
    SGImporter import (dbServiceFactory);
    auto status = import.Import (fileName);
    if (status == Status::STATUS_Failure) {
        std::cerr << "Import Failure !!!" << std::endl;
        throw std::runtime_error("Failed to import BDF file");
    }
}

TEST (ComputeNodeLoadTest, Force1Test)
 {
    // Input
    DB_SERVICEFACTORY->ClearDatabase();

    auto fileName = SG::Utility::FileSystem::getTestDataPath("SGFem/AnalysisProcess/") + "Force1_cquad4.bdf";
    std::string dbName = "Force1Test";

    GetModel (fileName, dbName, DB_SERVICEFACTORY);

    initialize (DB_SERVICEFACTORY);
    auto& assembly = AssemblyEigen::GetInstance();
    assembly.Assembly(DB_SERVICEFACTORY);

    // Test 模拟计算集中力
    LoadCalculator::ComputeStaticLoad (DB_SERVICEFACTORY, {1});
    assembly.Constraint (DB_SERVICEFACTORY, 1);

    // 计算载荷向量
    SG::FEMSolver::EigenVector fTest;

    // 计算非Load组合对应的载荷右端项
    assembly.ComputeBVector(1, _OUT fTest);

    // Target data 每个结点6个自由度，4个结点，共 6* 4= 24个自由度
    // 荷载作用在3号结点
    // 1号结点约束，则0-5为2号结点的数据，6-11为3号结点的数据
    Real fTarget1 = 15.6173761889; // 17号结点位移对应荷载（力）1
    Real fTarget2 = -31.2347523777; // 17号结点位移对于荷载（力）2
    Real fTarget3 = 93.7042571332; // 17号结点位移对应荷载（力）3     

    // Check
    // 测试17号节点上载荷， 前面的 “3       4       8      13      16” 5个节点为固定约束
    ASSERT_NEAR(fTarget1, fTest(66), TOLERANCE);
    ASSERT_NEAR(fTarget2, fTest(67), TOLERANCE);
    ASSERT_NEAR(fTarget3, fTest(68), TOLERANCE);
}


TEST (ComputeNodeLoadTest, Moment1Test)
 {
    // Input
    DB_SERVICEFACTORY->ClearDatabase();
    auto fileName = SG::Utility::FileSystem::getTestDataPath("SGFem/AnalysisProcess/") + "Moment1_cquad4.bdf";
    std::string dbName = "Moment1_cquad4";

    GetModel (fileName, dbName, DB_SERVICEFACTORY);

    initialize (DB_SERVICEFACTORY);

    auto& assembly = AssemblyEigen::GetInstance();
    assembly.Assembly(DB_SERVICEFACTORY);

    // Test 模拟计算集中力
    LoadCalculator::ComputeStaticLoad (DB_SERVICEFACTORY, {1});
    assembly.Constraint (DB_SERVICEFACTORY, 1);

    // 计算载荷向量
    SG::FEMSolver::EigenVector fTest;
    assembly.ComputeBVector(1, _OUT fTest);

    // 1号结点约束，则0-5为2号结点的数据，6-11为3号结点的数据
    Real fTarget1 = 15.6173761889; // 17号结点位移对应荷载（力）1
    Real fTarget2 = -31.2347523777; // 17号结点位移对于荷载（力）2
    Real fTarget3 = 93.7042571332; // 17号结点位移对应荷载（力）3     

    // Check
    // 测试17号节点上载荷， 前面的 “3       4       8      13      16” 5个节点为固定约束
    ASSERT_NEAR(fTarget1, fTest(69), TOLERANCE);
    ASSERT_NEAR(fTarget2, fTest(70), TOLERANCE);
    ASSERT_NEAR(fTarget3, fTest(71), TOLERANCE);
}


TEST (ComputeNodeLoadTest, Force2Test)
 {
    // Input
    auto fileName = SG::Utility::FileSystem::getTestDataPath("SGFem/AnalysisProcess/") + "Force2_NodalForce.bdf";
    std::string dbName = "Force2_NodalForce";

    GetModel (fileName, dbName, DB_SERVICEFACTORY);

    initialize (DB_SERVICEFACTORY);

    auto& assembly = AssemblyEigen::GetInstance();
    assembly.Assembly(DB_SERVICEFACTORY);

    // Test 模拟计算集中力
    LoadCalculator::ComputeStaticLoad (DB_SERVICEFACTORY, {1});
    assembly.Constraint (DB_SERVICEFACTORY, 1);


    // 计算载荷向量
    SG::FEMSolver::EigenVector fTest;

    // 计算非Load组合对应的载荷右端项
    assembly.ComputeBVector(1, _OUT fTest);

    // Target data 每个结点6个自由度，4个结点，共 6* 4= 24个自由度
    // 荷载作用在3号结点
    // 1号结点约束，则0-5为2号结点的数据，6-11为3号结点的数据
    // SG::FEMSolver::EigenVector fTarget(150);
    // fTarget.setZero(150);
    Real fTarget1 = 0; // 17号结点位移1
    Real fTarget2 = -87.41572761; // 17号结点位移2
    Real fTarget3 = 48.56429312; // 17号结点位移3

    // Check
    ASSERT_NEAR(fTarget1, fTest(6), TOLERANCE);
    ASSERT_NEAR(fTarget2, fTest(7), TOLERANCE);
    ASSERT_NEAR(fTarget3, fTest(8), TOLERANCE);
}

TEST (ComputeNodeLoadTest, Moment2Test)
{
    auto fileName = SG::Utility::FileSystem::getTestDataPath("SGFem/AnalysisProcess/") + "Moment2_NodalForce.bdf";
    std::string dbName = "Moment2_NodalForce";

    GetModel (fileName, dbName, DB_SERVICEFACTORY);

    initialize (DB_SERVICEFACTORY);
        
    // Input
    auto& assembly = AssemblyEigen::GetInstance();
    assembly.Assembly(DB_SERVICEFACTORY);

    // Test 模拟计算集中力
    LoadCalculator::ComputeStaticLoad (DB_SERVICEFACTORY, {1});
    assembly.Constraint (DB_SERVICEFACTORY, 1);


    // 计算载荷向量
    SG::FEMSolver::EigenVector fTest;

    // 计算非Load组合对应的载荷右端项
    assembly.ComputeBVector(1, _OUT fTest);

    // 荷载作用在3号结点
    // 1号结点约束，则为2号结点对应自由度为 0-5，3号结点对应的自由度为6-11

    Real fTarget1 = 0; // 3号结点4号力
    Real fTarget2 = -87.41572761; // 3号结点力5号力
    Real fTarget3 = 48.56429312; // 3号结点力6号力

    // Check
    ASSERT_NEAR(fTarget1, fTest(9), TOLERANCE);
    ASSERT_NEAR(fTarget2, fTest(10), TOLERANCE);
    ASSERT_NEAR(fTarget3, fTest(11), TOLERANCE);
}